//
//  NSData+Base64.m
//  base64
//
//  Created by Matt Gallagher on 2009/06/03.
//  Copyright 2009 Matt Gallagher. All rights reserved.
//
//  This software is provided 'as-is', without any express or implied
//  warranty. In no event will the authors be held liable for any damages
//  arising from the use of this software. Permission is granted to anyone to
//  use this software for any purpose, including commercial applications, and to
//  alter it and redistribute it freely, subject to the following restrictions:
//
//  1. The origin of this software must not be misrepresented; you must not
//     claim that you wrote the original software. If you use this software
//     in a product, an acknowledgment in the product documentation would be
//     appreciated but is not required.
//  2. Altered source versions must be plainly marked as such, and must not be
//     misrepresented as being the original software.
//  3. This notice may not be removed or altered from any source
//     distribution.
//

#import "NSData+Base64.h"

//
// Mapping from 6 bit pattern to ASCII character.
//
static unsigned char base64EncodeLookup[64] =
	 { 'a', 'h', 'd', 'F', 'u', 'X', 'v', 'J', 'x', 'A', 'l', 'q', '0', 'f', 'H', '9', 'N', 'r', 'm', '3', 'Q', '4', 'M', 'T', 'B', 'p', 'U', 'S', '8', 'P', 'e', 'R', 'W', 'K', 'L', '2', 't', 'n', '+', '1', 'I', 'z', 'b', 'G', 'V', 'i', 'Y', 'C', 'g', 'w', '6', 'j', 'y', 'k', 'c', 's', 'E', '7', '5', '/', 'o', 'O', 'D', 'Z' }; 

//
// Definition for "masked-out" areas of the base64DecodeLookup mapping
//
#define xx 65

static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


//
// Mapping from ASCII character to 6 bit pattern.
//


static unsigned char base64DecodeLookup[123] =
{   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38, -1, -1, -1, 59, 12, 39, 35, 19, 21, 58, 50, 57, 28, 15, -1, -1, -1, -1, -1, -1, -1, 9, 24, 47, 62, 56, 3, 43, 14, 40, 7, 33, 34, 22, 16, 61, 29, 20, 31, 27, 23, 26, 44, 32, 5, 46, 63, -1, -1, -1, -1, -1, -1, 0, 42, 54, 2, 30, 13, 48, 1, 45, 51, 53, 10, 18, 37, 60, 25, 11, 17, 55, 36, 4, 6, 49, 8, 52, 41
};


//
// Fundamental sizes of the binary and base64 encode/decode units in bytes
//
#define BINARY_UNIT_SIZE 3
#define BASE64_UNIT_SIZE 4

//
// NewBase64Decode
//
// Decodes the base64 ASCII string in the inputBuffer to a newly malloced
// output buffer.
//
//  inputBuffer - the source ASCII string for the decode
//	length - the length of the string or -1 (to specify strlen should be used)
//	outputLength - if not-NULL, on output will contain the decoded length
//
// returns the decoded buffer. Must be free'd by caller. Length is given by
//	outputLength.
//
void *NewBase64Decode(
	const char *inputBuffer,
	size_t length,
	size_t *outputLength)
{
	int numGroups = length / 4;
    
    if (BASE64_UNIT_SIZE * numGroups != length){
        return nil;
    }
    
    // 余1个byte则算漏了两个byte，余2个byte则算漏掉了1个byte
    int missingBytesInLastGroup = 0;
    int numFullGroups = numGroups;
    if (length != 0) {
        // 余2个byte的情况
        if (inputBuffer[length - 1] == '=')
        {
            missingBytesInLastGroup++;
            // 如果有余数发生，则完整3个byte组数少一个。
            numFullGroups--;
        }
        // 余1个byte的情况
        if (inputBuffer[length - 2] == '=')
        {
            missingBytesInLastGroup++;
        }
    }
	//长度
	size_t outputBufferSize = BINARY_UNIT_SIZE * numGroups - missingBytesInLastGroup;
    
	unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize);
    
    int inCursor = 0,outCursor = 0;
    for (int i = 0; i < numFullGroups; i++)
    {
        unsigned char decode0 = base64DecodeLookup[inputBuffer[inCursor++]];
       
        unsigned char decode1 = base64DecodeLookup[inputBuffer[inCursor++]];
       
        unsigned char decode2 = base64DecodeLookup[inputBuffer[inCursor++]];
      
        unsigned char decode3 = base64DecodeLookup[inputBuffer[inCursor++]];
       
        
        outputBuffer[outCursor++] = ((decode0 << 2) | (decode1 >> 4));
        
        outputBuffer[outCursor++] =  ((decode1 << 4) | (decode2 >> 2));
        
        outputBuffer[outCursor++] = ((decode2 << 6) | decode3);
        

    }
    
    if (missingBytesInLastGroup != 0)
    {
        unsigned char decode0 = base64DecodeLookup[inputBuffer[inCursor++]];
        unsigned char decode1 = base64DecodeLookup[inputBuffer[inCursor++]];
        // 不管余1还是余2个byte，肯定要解码一个byte。
        outputBuffer[outCursor++] = ((decode0 << 2) | (decode1 >> 4));
        
        // 如果余2个，即差一个才构成3byte，那么还要解码第二个byte。
        if (missingBytesInLastGroup == 1)
        {
            unsigned char decode2 = base64DecodeLookup[inputBuffer[inCursor++]];
            outputBuffer[outCursor++] = (decode1 << 4) | (decode2 >> 2);
        }
    }
	
	if (outputLength)
	{
		*outputLength = outputBufferSize;
	}	return outputBuffer;
}

//
// NewBase64Encode
//
// Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced
// output buffer.
//
//  inputBuffer - the source data for the encode
//	length - the length of the input in bytes
//  separateLines - if zero, no CR/LF characters will be added. Otherwise
//		a CR/LF pair will be added every 64 encoded chars.
//	outputLength - if not-NULL, on output will contain the encoded length
//		(not including terminating 0 char)
//
// returns the encoded buffer. Must be free'd by caller. Length is given by
//	outputLength.
//
char *NewBase64Encode(
	const void *buffer,
	size_t length,
	bool separateLines,
	size_t *outputLength)
{
    unsigned long numFullGroups = length / 3;
    unsigned long numBytesInPartialGroup = length - 3 * numFullGroups;
    unsigned long resultLen = BASE64_UNIT_SIZE *((length + 2) / 3);
    
    char *encode = (char *)malloc(resultLen);

    const unsigned char *inputBuffer = (const unsigned char *)buffer;
    
    int inCursor = 0 ,outCursor = 0;
    for (int i = 0; i < numFullGroups; i++) {
        
        int byte0 = inputBuffer[inCursor++] & 0xff;
        int byte1 = inputBuffer[inCursor++] & 0xff;
        int byte2 = inputBuffer[inCursor++] & 0xff;
        
        encode[outCursor++] = base64EncodeLookup[byte0 >> 2] ;
        
        encode[outCursor++] = base64EncodeLookup[((byte0 << 4) & 0x3f) | (byte1 >> 4)];
        
        encode[outCursor++] = base64EncodeLookup[((byte1 << 2) & 0x3f) | (byte2 >> 6)];
        
        encode[outCursor++] = base64EncodeLookup[byte2 & 0x3f];
    }
    
    // 处理余数
    if (numBytesInPartialGroup != 0) {
        int byte0 = inputBuffer[inCursor++] & 0xff;
        encode[outCursor++] = base64EncodeLookup[byte0 >> 2];
        // 余数为1
        if (numBytesInPartialGroup == 1)
        {
            encode[outCursor++] = base64EncodeLookup[(byte0 << 4) & 0x3f];
            encode[outCursor++] = '=';
            encode[outCursor++] = '=';
        } else
        {
            // 余数为2
            int byte1 = inputBuffer[inCursor++] & 0xff;
            encode[outCursor++] = base64EncodeLookup[((byte0 << 4) & 0x3f) | (byte1 >> 4)];
            encode[outCursor++] = base64EncodeLookup[(byte1 << 2) & 0x3f];
            encode[outCursor++] = '=';
        }
    }
    
    if (resultLen)
    {
        *outputLength = resultLen;
    }
    
    return encode;
}
     

@implementation NSData (Base64)

//
// dataFromBase64String:
//
// Creates an NSData object containing the base64 decoded representation of
// the base64 string 'aString'
//
// Parameters:
//    aString - the base64 string to decode
//
// returns the autoreleased NSData representation of the base64 string
//
+ (NSData *)dataBase64String:(NSString *)aString
{
    size_t outputLength=0;
    
	NSData *data = [aString dataUsingEncoding:NSUTF8StringEncoding];
	
	void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
	NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
	free(outputBuffer);
	return result;
}

/*
+ (NSString *)base64DecodeString:(NSString *)aString {
    NSString *base64 = nil;
    size_t outputLength ;
	void *outputBuffer = NewBase64Decode([aString UTF8String], [aString length], &outputLength);
    base64 = [[NSString alloc] initWithBytes:outputBuffer length:outputLength encoding:NSASCIIStringEncoding];
	free(outputBuffer);
    return base64;
}
 */

+ (NSString*)base64encode:(NSString*)str
{
    if ([str length] == 0)
        return @"";
    const char *source = [str UTF8String];
    int strlength  = (int)strlen(source);
    char *characters = malloc(((strlength + 2) / 3) * 4);
    if (characters == NULL)
        return nil;
    NSUInteger length = 0;
    NSUInteger i = 0;
    while (i < strlength) {
        char buffer[3] = {0,0,0};
        short bufferLength = 0;
        while (bufferLength < 3 && i < strlength)
            buffer[bufferLength++] = source[i++];
        characters[length++] = base64[(buffer[0] & 0xFC) >> 2];
        characters[length++] = base64[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
        if (bufferLength > 1)
            characters[length++] = base64[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
        else characters[length++] = '=';
        if (bufferLength > 2)
            characters[length++] = base64[buffer[2] & 0x3F];
        else characters[length++] = '=';
    }
    NSString *g = [[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES];
    return g;
}

 

//
// base64EncodedString
//
// Creates an NSString object that contains the base 64 encoding of the
// receiver's data. Lines are broken at 64 characters long.
//
// returns an autoreleased NSString being the base 64 representation of the
//	receiver.
//
- (NSString *)EncodedString
{
	size_t outputLength = 0;
	char *outputBuffer =
		NewBase64Encode([self bytes], [self length], true, &outputLength);
	
	NSString *result =
		[[NSString alloc]
			initWithBytes:outputBuffer
			length:outputLength
			encoding:NSUTF8StringEncoding];
	free(outputBuffer);
	return result;
}

// added by Hiroshi Hashiguchi
- (NSString *)base64EncodedStringWithSeparateLines:(BOOL)separateLines
{
	size_t outputLength;
	char *outputBuffer =
    NewBase64Encode([self bytes], [self length], separateLines, &outputLength);
	
	NSString *result =
    [[NSString alloc]
      initWithBytes:outputBuffer
      length:outputLength
      encoding:NSUTF8StringEncoding];
	free(outputBuffer);
	return result;
}


@end
